package com.k8.common.aop.internal.pointcut;

import com.k8.common.aop.internal.exception.PointCutResolveException;

import java.lang.reflect.Method;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


//如果有包，第一个包名必须写
public class DefaultPointCut implements PointCut {
    private List<PackageNode> packages;
    //fullName不能是包名开头不能是..
    private boolean anyReturn = false;
    private String returnType;
    private boolean anyArgs = false;
    private boolean emptyArg = false;
    private String[] argTypes;
    private String simpleName;
    private String methodName;
    private final static Pattern compile = Pattern.compile("^([a-zA-Z]+|\\*)\\s(([a-zA-Z]+(\\.|(\\.\\.)))+(([a-zA-Z]+|\\*)(\\.|(\\.\\.)))*)*([a-zA-Z]+|\\*)\\.([a-zA-Z]+|\\*)\\(((\\.\\.)?|(([a-zA-Z]+|\\*)(,([a-zA-Z]+|\\*))*))\\)$");

    //无法匹配任意如 * *..*.*() 任意包下的任意类的任意方法，没有意义，直接排除
    //* *.*() 没有包 根目录下的任意类的任意方法
    //* com.*.*() 有包必须以第一个包名开头，com包下的任意类的任意方法
    //* com..*.*() com包及其子包下的任意类的任意方法  tag=true
    //* com.*.*.*() com包下的任意子包的下的任意子类
    //* com..*.*.*()  包名 com..*  传入类必须在com包下任意*包下
    public DefaultPointCut(String signature) throws PointCutResolveException {
        if (signature == null || signature.isEmpty()) {
            throw new PointCutResolveException(signature);
        }
        Matcher matcher = compile.matcher(signature);
        if (!matcher.matches()) {
            throw new PointCutResolveException(signature);
        }
        String[] split = Arrays.stream(signature.split(" ")).filter(s -> {
            return !s.isEmpty();
        }).toArray(String[]::new);
        String returnTypeS = split[0];
        if ("*".equals(returnTypeS)) {
            anyReturn = true;
        } else {
            this.returnType = returnTypeS;
        }
        ;
        String full = split[1];
        //切割fullName.methodName和args
        String[] split2 = Arrays.stream(full.split("\\(")).filter(s -> {
            return !s.isEmpty();
        }).toArray(String[]::new);
        String tFull = split2[0];
        String args = split2[1].substring(0, split2[1].length() - 1);//去除)
        int lastDolt = tFull.lastIndexOf(".");//切割fullName和methodName
        String fullName = tFull.substring(0, lastDolt);
        this.methodName = tFull.substring(lastDolt + 1); //* 或 methodName
        int lastDolt2 = fullName.lastIndexOf(".");//切割simpleName和package
        if (lastDolt2 < 0) {//说明fullname=*或者simpleName
            this.simpleName = fullName;
        } else {
            this.simpleName = fullName.substring(lastDolt2 + 1);
            String packageAllS = fullName.substring(0, lastDolt2 + 1);//保留最后的点
            this.packages = new ArrayList<>();
            char[] charArray = packageAllS.toCharArray();
            boolean isDolt = false;
            CharBuffer charBuffer = CharBuffer.allocate(charArray.length);
            for (char c : charArray) {
                if (c != '.') {
                    charBuffer.put(c);
                } else if (isDolt) {
                    isDolt = false;
                    packages.add(new PackageNode(PackageNode.PackageType.include));
                } else {
                    isDolt = true;//第一个.开始收集
                    charBuffer.flip();
                    String value = charBuffer.toString();
                    if ("*".equals(value)) {
                        packages.add(new PackageNode(PackageNode.PackageType.Any));
                    } else {
                        packages.add(new PackageNode(PackageNode.PackageType.defaultNode, value));
                    }
                    charBuffer.clear();
                }
            }
        }
        if ("..".equals(args)) {
            anyArgs = true;
        } else if (args.isEmpty()) {
            emptyArg = true;
        } else if (!args.contains(",")) {
            this.argTypes = new String[1];
            argTypes[0] = args;
        } else {
            this.argTypes = Arrays.stream(args.split(",")).filter(s -> {
                return !s.isEmpty();
            }).toArray(String[]::new);
        }
    }

    @Override
    public boolean match(Method method) {
        Class<?> clazz = method.getDeclaringClass();
        String fullName = clazz.getName();
        String simpleName = clazz.getSimpleName();
        Class<?> returnType = method.getReturnType();
        String methodName1 = method.getName();
        Class[] paras = Arrays.stream(method.getParameterTypes()).toArray(Class[]::new);
        if (!"*".equals(this.simpleName) && !simpleName.equals(this.simpleName)) {
            return false;
        }
        if (!"*".equals(this.methodName) && !methodName1.equals(this.methodName)) return false;
        if (!anyArgs) {
            if (emptyArg) {
                if (paras.length > 0) return false;
            } else if (paras.length != this.argTypes.length) return false;
            else {
                int i = 0;
                for (String argType : this.argTypes) {
                    boolean b = false;
                    if (paras[i].isPrimitive() || paras[i].equals(String.class)) {//基础类型或String可忽略大小写
                        b = paras[i].getSimpleName().equalsIgnoreCase(argType);
                    } else {
                        b = paras[i].getSimpleName().equals(argType);
                    }
                    if (!b) return false;
                }
            }
        }
        if (!anyReturn) {
            boolean b = false;
            if (returnType.isPrimitive() || returnType.equals(String.class)) {
                b = returnType.getSimpleName().equalsIgnoreCase(this.returnType);
            } else {
                b = returnType.getSimpleName().equals(this.returnType);
            }
            if (!b) return false;
        }
        //比较包前缀
        if (fullName.equals(simpleName)) {//该类在类路径根目录下,不能有包,如果没有包即==null则判断成功
            if (this.packages != null) return false;
            else return true; //前面已经比较过simpleName
        } else {
            final int index = fullName.lastIndexOf(".");
            String[] packageNames = fullName.substring(0, index).split("\\.");//提取全部包名并拆散
            int start = 0;
            int start2 = 0;
            int goT1 = -1;//切点回溯点
            int goT2 = -1;//匹配的包的回溯点
            while (start < packages.size() && start2 < packageNames.length) {
                PackageNode packageNode = packages.get(start);
                String packageName = packageNames[start2];
                switch (packageNode.getType()) {
                    case Any:
                        start++;
                        start2++;
                        break;
                    case defaultNode:
                        String value = packageNode.getValue();
                        if (packageName.equals(value)) {
                            start++;
                            start2++;
                            break;
                        } else if (goT1 < 0) {//没有回溯点直接失败
                            return false;
                        } else {
                            start = goT1 + 1;//切点相当于回到子串开头
                            goT2++;//匹配者相当于字符串匹配的长串，上一个已经作废（d2作废，从？开始匹配）
                            start2 = goT2;
                            break;
                        }
                    case include:  //com..a com.a
                        if (start == packages.size() - 1) return true; //走到了终点且是包含关系，当前包和子包都匹配
                        //a..b.v.c..? a.d1.e.b.v.c.d2.?
                        //切点回溯点切换到..位置
                        // 匹配者回溯点从d1跳到d2，相当于截取，贪心思想，切点的list每两个..之间为一段，每一段尽量和匹配者的前面段落绑定
                        //如果前面段落绑定了会失败，那么就算绑定后面的也会失败，因为是向后找
                        goT1 = start;
                        goT2 = start2;
                        start++;
                        break;
                }
            }
            if (start2 == packageNames.length) {//匹配者先终止
                //?.x ?.x
                if (start == packages.size()) return true;//共同匹配到结束
                    // a.b..Class a.b.Class 这种情况则start位置在packages.size()-1
                else if (start == packages.size() - 1 && packages.get(start).getType() == PackageNode.PackageType.include)
                    return true;
                else return false;
            } else {//切点先到终点，且匹配者未到终点，只有当最后一个为include时才算通过
                if (packages.get(packages.size() - 1).getType() == PackageNode.PackageType.include) return true;
                return false;
            }
        }
    }
}
